package dev.hxliu.cn.javassist;

import dev.hxliu.cn.inject.InjectMethod;
import dev.hxliu.cn.inject.InjectMethodTypeEnum;
import dev.hxliu.cn.inject.InjectValue;
import dev.hxliu.cn.vo.MerchantUserPageVO;
import javassist.*;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.MethodInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.UnaryOperator;

public class AdapterFactory {

    // 初始化pool
    public static ClassPool initPool(){
        ClassPool   pool = ClassPool.getDefault();
        // 添加应用所需的包
        pool.importPackage(AdapterFactory.class.getPackageName());
        return pool;
    }

    public static Field[] getFieldsDirectly(Class<?> beanClass, boolean withSuperClassFields) throws SecurityException {
        Assert.notNull(beanClass,"beanClass must not be null");

        Field[] allFields = null;
        Class<?> searchType = beanClass;
        Field[] declaredFields;
        while (searchType != null) {
            declaredFields = searchType.getDeclaredFields();
            if (null == allFields) {
                allFields = declaredFields;
            } else {
                Field[] result = Arrays.copyOf(allFields, allFields.length + declaredFields.length);
                System.arraycopy(declaredFields, 0, result, allFields.length, declaredFields.length);
                allFields = result;
            }
            searchType = withSuperClassFields ? searchType.getSuperclass() : null;
        }

        return allFields;
    }
    public static  AdapterClass[] getAdapterClass(Class<?>clazz){
        Field[] fields = getFieldsDirectly(clazz, true);
        return Arrays.stream(fields).parallel().map(field->{
                    InjectValue injectValue = field.getAnnotation(InjectValue.class);
                    if(injectValue != null) {
                        Class<? extends InjectMethod<?>>  supplier = injectValue.value();
                        try {
                            PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(clazz, field.getName());
                            if(propertyDescriptor!=null) {

                                InjectMethod<?> injectMethod = injectValue.createType().getConvertType().apply(supplier);
                                InjectMethodTypeEnum typeEnum = initTypeEnum(supplier);
                                return new AdapterClass(injectValue.value(),injectMethod,typeEnum,field.getName(),injectValue.isCover());
                            }
                        } catch (Throwable e) {
                            throw new RuntimeException(e);
                        }
                    }
                    return null;
    }).filter(Objects::nonNull).toArray(AdapterClass[]::new);
    }

    private static InjectMethodTypeEnum initTypeEnum(Class<? extends InjectMethod<?>> supplier) throws Throwable {
        ResolvableType resolvableType = ResolvableType.forClass(supplier);
        ResolvableType injectMethodType = resolvableType.as(InjectMethod.class);
        ResolvableType genericType = injectMethodType.getGeneric();
        InjectMethodTypeEnum result ;
        Class<?> rawClass = genericType.getRawClass();
        if (rawClass != null) {
            if (UnaryOperator.class.isAssignableFrom(rawClass)) {
                result = InjectMethodTypeEnum.InjectMethodArgColumn;
            }else if (Function.class.isAssignableFrom(rawClass)) {
                result = InjectMethodTypeEnum.InjectMethodArgObject;
            }else{
                result= InjectMethodTypeEnum.InjectMethod;
            }
            return result;
        }
        return InjectMethodTypeEnum.NONE;
    }

    /**
     * 创建适配器
     * @param clazz 需要适配的注入类
     * @param args 适配器需要的初始化对象及方法
     * @return 适配器实例
     * @param <T> 适配的注入类
     * @throws Exception
     */
    public static  <T> InjectAdapter<T> createAdapter(Class<T> clazz, AdapterClass... args)
            throws Exception {

        Class<?> result = createAdapterClass(clazz,args);
        // 构造对象传入进行初始化
        Class<?> [] argTypes = new Class<?>[args.length];
        Object[] argValues = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].argClass();
            argValues[i] = args[i].instance();
        }
        @SuppressWarnings("unchecked")
        InjectAdapter<T> tInjectAdapter = (InjectAdapter<T>) result.getConstructor(argTypes).newInstance(argValues);
        return tInjectAdapter;
    }
    public static final Map<Class<?>, InjectAdapter<?>> INJECT_VALUE_CACHE = new ConcurrentHashMap<>();
    public static  <T> InjectAdapter<T> createAdapter(Class<T> clazz)
             {
        @SuppressWarnings("unchecked")
        InjectAdapter<T> injectAdapter = (InjectAdapter<T>)INJECT_VALUE_CACHE.computeIfAbsent((Class<?>)clazz, key -> {
            try {
                return createAdapter(key, getAdapterClass(key));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

        return injectAdapter;
//            return createAdapter(clazz,getAdapterClass(clazz));
    }

    public static void main(String[] args) throws Exception {
        InjectAdapter<MerchantUserPageVO> injectAdapter = AdapterFactory.createAdapter(MerchantUserPageVO.class);
        MerchantUserPageVO obj = new MerchantUserPageVO();
        injectAdapter.inject(obj);
        System.out.println(obj);
    }

    private static void createBridgeMethod(CtClass cc,ClassPool pool,CtClass merchantClass) throws CannotCompileException, NotFoundException {

        // 关键修复：使用正确的语法调用公共方法
        // 确保字符串拼接正确，避免出现语法错误
        String methodBody = String.format("""
                {
                    this.injectThis((%s)$1);
                }
                """, merchantClass.getName(), merchantClass.getName(), merchantClass.getName());

        // 创建桥接方法
        CtMethod bridgeMethod = CtNewMethod.make(
                CtClass.voidType, // 返回类型
                "inject",                                    // 方法名
              new CtClass[]{  pool.get("java.lang.Object")},                                        // 参数类型数组
                null,                                        // 异常类型数组
                methodBody,                                  // 方法体
                cc                                           // 所属类
        );

        // 设置方法访问标志
        MethodInfo methodInfo = bridgeMethod.getMethodInfo();
        methodInfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.BRIDGE | AccessFlag.SYNTHETIC);

        cc.addMethod(bridgeMethod);
    }
    /**
     * 适配器类
     * @param argClass 参数类型
     * @param instance 参数实例
     */
    public record AdapterClass(Class<?> argClass, Object instance, InjectMethodTypeEnum typeEnum,String fileName,boolean isCover) {

    }
    /**
     * 创建 MerchantUserPageVO 专用的注入适配器实现类
     *
     * @return 适配器类的字节码（可实例化）
     */
    public static Class<?> createAdapterClass(Class<?>clazz,AdapterClass... args) throws Exception {ClassPool pool = initPool();
        String clazzName = clazz.getName();
        // 创建类时直接指定完整类名（包含包名）
        CtClass ctClass = pool.makeClass(AdapterFactory.class.getPackageName() + "InjectAdapter_" + clazzName.replace('.', '_'));
        // 导入引用的文件
        pool.importPackage(clazz.getName());

        // 添加泛型接口：InjectAdapter<MerchantUserPageVO>
        CtClass injectAdapter = pool.get(InjectAdapter.class.getName());
        CtClass merchantClass = pool.get(clazzName);

        // 设置接口
        ctClass.setInterfaces(new CtClass[]{injectAdapter});
        // 生成泛型签名
        String genericSignature = "Ldev/hxliu/cn/javassist/InjectAdapter<L" + clazzName.replace('.', '/') + ";>;";
        ctClass.setGenericSignature(genericSignature);

        CtClass[] constructor = new CtClass[args.length];
        // 添加内容
        StringBuilder methodBodyBuilder = new StringBuilder();
        StringBuilder constructorBodyBuilder = new StringBuilder();
        methodBodyBuilder.append("{");
        constructorBodyBuilder.append("{");
        for (int i = 0; i < args.length; i++) {
            AdapterClass adapterClass = args[i];

            pool.importPackage(adapterClass.argClass.getName());
            appendConstructorBodyBuilder(adapterClass, constructorBodyBuilder, i);
            appendMethodBodyBuild(adapterClass, methodBodyBuilder, clazz, pool);

            String name = adapterClass.argClass().getName();
            constructor[i] = pool.get(name);
            // 添加字段
            CtField argsField = new CtField(pool.get(adapterClass.argClass.getName()),
                    StringUtils.uncapitalize(adapterClass.argClass.getSimpleName()),
                    ctClass);
            argsField.setModifiers(Modifier.PRIVATE | Modifier.FINAL);
            ctClass.addField(argsField);
        }
        methodBodyBuilder.append("}");
        constructorBodyBuilder.append("}");

        // 添加构造函数
        CtConstructor ctor = new CtConstructor(
                constructor, ctClass
        );

        ctor.setBody(constructorBodyBuilder.toString());

        ctClass.addConstructor(ctor);

        // 添加inject方法，确保方法签名正确
        CtMethod method = new CtMethod(
                CtClass.voidType,
                "injectThis",
                new CtClass[]{merchantClass}, // 确保参数类型和接口一致
                ctClass
        );
        method.setBody(methodBodyBuilder.toString());
        ctClass.addMethod(method);
        createBridgeMethod(ctClass,pool,merchantClass);
        writeClassForDebugging(ctClass);
        return ctClass.toClass();
    }

    // 调试辅助：写入生成的.class文件
    private static void writeClassForDebugging(CtClass cc) {
        try {
            cc.writeFile("target/generated-classes");
        } catch (CannotCompileException | IOException e) {
            // 忽略写入失败，不影响功能
        }
    }

    public static void appendConstructorBodyBuilder(AdapterClass adapterClass, StringBuilder stringBuilder,int i){
        String uncapitalize = StringUtils.uncapitalize(adapterClass.argClass.getSimpleName());
        stringBuilder.append("this.").append(uncapitalize).append(" = $").append(i+1).append(";");
    }

    public static void appendMethodBodyBuild(AdapterClass adapterClass, StringBuilder stringBuilder,Class<?>clazz,ClassPool pool){
//        String uncapitalize = StringUtils.uncapitalize(adapterClass.argClass.getSimpleName());
//        String capitalize = StringUtils.capitalize(adapterClass.fileName);
//        switch (adapterClass.typeEnum){
//            case InjectMethodArgObject->
//                    stringBuilder.append(String.format("""
//                    $1.%s(%s.inject().apply($1));
//                    """, "set"+capitalize,uncapitalize));
//            case InjectMethod ->
//                    stringBuilder.append(String.format("""
//                    $1.%s(%s.inject());
//                    """, "set"+capitalize,uncapitalize));
//            case InjectMethodArgColumn->
//                    stringBuilder.append(String.format("""
//                    $1.%s(%s.inject().apply($1.%s()));
//                    """, "set"+capitalize,uncapitalize,"get"+capitalize));
//        }
        String uncapitalize = StringUtils.uncapitalize(adapterClass.argClass.getSimpleName());
        String capitalize = StringUtils.capitalize(adapterClass.fileName);
        if(!adapterClass.isCover){
            stringBuilder.append(String.format("""
                            if($1.%s()==null){
                            ""","get" + capitalize));
        }
        switch (adapterClass.typeEnum) {
            case InjectMethodArgObject ->
            {
                // 进行类型转换
                fileNames fieldType = getFieldType(adapterClass.fileName, clazz);
                pool.importPackage(fieldType.fieldName());

                stringBuilder.append(String.format("""
                    $1.%s((%s) %s.inject().apply($1));
                    """, "set" + capitalize, fieldType.fieldSimpleName, uncapitalize));

            }
            case InjectMethod ->
            {
                fileNames fieldType = getFieldType(adapterClass.fileName, clazz);
                pool.importPackage(fieldType.fieldName());
                // 进行类型转换
                    stringBuilder.append(String.format("""
                    $1.%s((%s) %s.inject());
                    """, "set" + capitalize, fieldType.fieldSimpleName, uncapitalize));
            }
            case InjectMethodArgColumn -> {
                // 进行类型转换
                fileNames fieldType = getFieldType(adapterClass.fileName, clazz);
                pool.importPackage(fieldType.fieldName());
                // 进行类型转换
                stringBuilder.append(String.format("""
                        $1.%s((%s) %s.inject().apply($1.%s()));
                        """, "set" + capitalize, fieldType.fieldSimpleName, uncapitalize, "get" + capitalize));
            }

        }

        if(!adapterClass.isCover){
            stringBuilder.append("}");
        }
    }

    record fileNames(String fieldName,String fieldSimpleName){}

    /**
     * 根据字段名获取字段类型
     * @param fieldName 字段名
     * @param clazz 类类型
     * @return 字段类型的字符串表示
     */
    private static fileNames getFieldType(String fieldName, Class<?> clazz) {
        try {
            // 首字母小写
            String uncapitalizedFieldName = StringUtils.uncapitalize(fieldName);
            Field field = clazz.getDeclaredField(fieldName);
            Class<?> type = field.getType();
            return new fileNames(type.getName(), type.getSimpleName()) ;
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("Field " + fieldName + " not found in class " + clazz.getName(), e);
        }
    }

}
